//
// Created by benxb on 2021/12/8.
//

#include <gtest/gtest.h>
#include <fstream>
#include <string>
#include <vector>
#include <catelog/column.h>
#include <catelog/table.h>
#include <mem/data_table_manager.h>
#include <common/logger.h>
#include <access/database.h>
#include <catelog/point_cloud.h>
#include <common/timer.h>

std::string file_name = "/home/chen/umm_000000_29.bin";
std::string copy_file = file_name+".copy";
std::string road_all = "/data/kitti_l.bin";

TEST(BinaryWriteTest, BatchWrite) {
    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    std::fstream copyf(copy_file, std::ios::binary | std::ios::trunc | std::ios::out);
    copyf.close();
    copyf.open(copy_file, std::ios::binary | std::ios::in | std::ios::out);
    std::vector<float> x_arr;
    std::vector<float> y_arr;
    std::vector<float> z_arr;
    std::vector<float> intensity_arr;

    benxdb::Timer timer;

    while (!io.eof()) {
        float x, y, z, i;
        io.read(reinterpret_cast<char *>(&x), sizeof(x));
        io.read(reinterpret_cast<char *>(&y), sizeof(y));
        io.read(reinterpret_cast<char *>(&z), sizeof(z));
        io.read(reinterpret_cast<char *>(&i), sizeof(i));
        x_arr.push_back(x);
        y_arr.push_back(y);
        z_arr.push_back(z);
        intensity_arr.push_back(i);

        if (x_arr.size() > 64000) {
            timer.Start();
            for(int k = 0; k < x_arr.size(); k++) {
                copyf.write((const char *) &x_arr[k], sizeof(x_arr[k]));
                copyf.write((const char *) &y_arr[k], sizeof(y_arr[k]));
                copyf.write((const char *) &z_arr[k], sizeof(z_arr[k]));
                copyf.write((const char *) &intensity_arr[k], sizeof(intensity_arr[k]));
            }
            copyf.flush();
            x_arr.clear();
            y_arr.clear();
            z_arr.clear();
            intensity_arr.clear();
            timer.Pause();
        }
    }
    std::cout << x_arr.size() << std::endl;
    timer.StopAndPrint("Test append total.");

    io.close();
    copyf.close();
}

TEST(BinaryReadTest, SimpleQuery) {
    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    std::vector<float> x_arr;
    std::vector<float> y_arr;
    std::vector<float> z_arr;
    std::vector<float> intensity_arr;

    int total_count = 0;
    while (!io.eof()) {
        total_count++;
        float x, y, z, i;
        io.read(reinterpret_cast<char *>(&x), sizeof(x));
        io.read(reinterpret_cast<char *>(&y), sizeof(y));
        io.read(reinterpret_cast<char *>(&z), sizeof(z));
        io.read(reinterpret_cast<char *>(&i), sizeof(i));
        if (x > -10 && x < 10 && y < 10 && y > -10) {
            x_arr.push_back(x);
            y_arr.push_back(y);
            z_arr.push_back(z);
            intensity_arr.push_back(i);
        }
    }
    std::cout << total_count << " " << x_arr.size() << std::endl;
    io.close();
}

TEST(AsciiWriteTest, BatchWrite) {
    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    std::fstream copyf(copy_file, std::ios::trunc | std::ios::out);
    copyf.close();
    copyf.open(copy_file, std::ios::binary | std::ios::in | std::ios::out);
    std::vector<float> x_arr;
    std::vector<float> y_arr;
    std::vector<float> z_arr;
    std::vector<float> intensity_arr;

    while (!io.eof()) {
        float x, y, z, i;
        io.read(reinterpret_cast<char *>(&x), sizeof(x));
        io.read(reinterpret_cast<char *>(&y), sizeof(y));
        io.read(reinterpret_cast<char *>(&z), sizeof(z));
        io.read(reinterpret_cast<char *>(&i), sizeof(i));
        x_arr.push_back(x);
        y_arr.push_back(y);
        z_arr.push_back(z);
        intensity_arr.push_back(i);
    }
    std::cout << x_arr.size() << std::endl;

    auto start = std::chrono::steady_clock::now();
    for(int i = 0; i < x_arr.size(); i++) {
        copyf << x_arr[i] << "," << y_arr[i] << "," << z_arr[i] << "," << intensity_arr[i] << std::endl;
    }
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
    std::cout << "BatchWrite End, spend " << time_span.count() << std::endl;
    io.close();
    copyf.close();
}

TEST(TableManagerTest, BatchWrite) {
    std::vector<benxdb::Column> columns;
    columns.emplace_back("x", benxdb::FLOAT);
    columns.emplace_back("y", benxdb::FLOAT);
    columns.emplace_back("z", benxdb::FLOAT);
    columns.emplace_back("intensity", benxdb::FLOAT);
    benxdb::DataTableManager manager(std::make_shared<benxdb::Table>("test", columns));

    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    std::vector<benxdb::Tuple> tuples;
    tuples.reserve(64000);
    benxdb::Timer timer;
    benxdb::Timer com_timer;
    std::vector<benxdb::Value> vals;
    while (!io.eof()) {
        com_timer.Start();
        float x, y, z, i;
        io.read(reinterpret_cast<char *>(&x), sizeof(x));
        io.read(reinterpret_cast<char *>(&y), sizeof(y));
        io.read(reinterpret_cast<char *>(&z), sizeof(z));
        io.read(reinterpret_cast<char *>(&i), sizeof(i));
        vals.emplace_back(benxdb::FLOAT, x);
        vals.emplace_back(benxdb::FLOAT, y);
        vals.emplace_back(benxdb::FLOAT, z);
        vals.emplace_back(benxdb::FLOAT, i);
        tuples.emplace_back(vals);
        vals.clear();
        com_timer.Pause();

        if (tuples.size() >= 64000) {
            timer.Start();
            manager.AppendManyTuple(tuples);
            timer.Pause();
            tuples.clear();
        }
    }
    if (!tuples.empty()) {
        manager.AppendManyTuple(tuples);
    }
    timer.StopAndPrint("Test append total.");
    com_timer.StopAndPrint("Compose total.");

//    std::cout << tuples.size() << std::endl;
//
//    auto start = std::chrono::steady_clock::now();
//    for (auto & tuple : tuples) {
//        manager.AppendTuple(tuple);
//    }
//    auto end = std::chrono::steady_clock::now();
//    std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
//    std::cout << "BatchWrite End, spend " << time_span.count() << std::endl;
    io.close();
}

/**
 * Table manager write point cloud
 */
TEST(TableManagerTest, WritePointCloud) {
    std::vector<benxdb::Column> columns;
    columns.emplace_back("x", benxdb::FLOAT);
    columns.emplace_back("y", benxdb::FLOAT);
    columns.emplace_back("z", benxdb::FLOAT);
    columns.emplace_back("intensity", benxdb::FLOAT);
    benxdb::DataTableManager manager(std::make_shared<benxdb::Table>("test", columns));

    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    benxdb::PointCloud<benxdb::PointXYZI> point_cloud;
    benxdb::Timer timer;
    benxdb::Timer com_timer;
    while (!io.eof()) {
        com_timer.Start();
        benxdb::PointXYZI point{};
        io.read(reinterpret_cast<char *>(&point.x), sizeof(point.x));
        io.read(reinterpret_cast<char *>(&point.y), sizeof(point.y));
        io.read(reinterpret_cast<char *>(&point.z), sizeof(point.z));
        io.read(reinterpret_cast<char *>(&point.intensity), sizeof(point.intensity));
        point_cloud.Append(point);
        com_timer.Pause();

        if (point_cloud.Size() >= 64000) {
            timer.Start();
            manager.AppendPointCloud(point_cloud);
            point_cloud.Clear();
            timer.Pause();
        }
    }
    if (!point_cloud.Empty()) {
        manager.AppendPointCloud(point_cloud);
    }
    timer.StopAndPrint("Test append total.");
    com_timer.StopAndPrint("Compose total.");
    io.close();
}


TEST(DatabaseTest, BatchWrite) {
    std::vector<benxdb::Column> columns;
    columns.emplace_back("x", benxdb::FLOAT);
    columns.emplace_back("y", benxdb::FLOAT);
    columns.emplace_back("z", benxdb::FLOAT);
    columns.emplace_back("intensity", benxdb::FLOAT);
    benxdb::Database db;
    db.CreateTable(std::make_shared<benxdb::Table>("test", columns));

    benxdb::PointCloud<benxdb::PointXYZI> point_cloud;

    std::fstream io(file_name, std::ios::binary | std::ios::in | std::ios::out);
    std::vector<benxdb::Tuple> tuples;
    int count = 0;

    while (!io.eof()) {
        count++;
        float x, y, z, i;
        io.read(reinterpret_cast<char *>(&x), sizeof(x));
        io.read(reinterpret_cast<char *>(&y), sizeof(y));
        io.read(reinterpret_cast<char *>(&z), sizeof(z));
//        io.read(reinterpret_cast<char *>(&i), sizeof(i));
        benxdb::PointXYZI point{};
        point.x = x;
        point.y = y;
        point.z = z;
        point.intensity = 2.3;
        point_cloud.Append(point);

        if (point_cloud.Size() >= 64000) {
            db.AppendPointCloud("test", point_cloud);
            point_cloud.Clear();
        }
    }

    if (!point_cloud.Empty()) {
        db.AppendPointCloud("test", point_cloud);
    }
    std::cout << count << std::endl;

//    auto start = std::chrono::steady_clock::now();
//    db.AppendPointCloud("test", point_cloud);
//    auto end = std::chrono::steady_clock::now();
//    std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
//    std::cout << "BatchWrite End, spend " << time_span.count() << std::endl;
    io.close();
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}